package io.geekidea.boot.generator.handler;

import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.InjectionConfig;
import com.baomidou.mybatisplus.generator.config.*;
import com.baomidou.mybatisplus.generator.config.po.TableField;
import com.baomidou.mybatisplus.generator.config.po.TableInfo;
import com.baomidou.mybatisplus.generator.config.rules.DateType;
import com.baomidou.mybatisplus.generator.config.rules.IColumnType;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import io.geekidea.boot.framework.exception.BusinessException;
import io.geekidea.boot.generator.config.GeneratorConfig;
import io.geekidea.boot.generator.enums.DatabaseType;
import io.geekidea.boot.generator.enums.DefaultOrderType;
import io.geekidea.boot.generator.query.CustomMySqlQuery;
import io.geekidea.boot.generator.query.CustomOracleQuery;
import io.geekidea.boot.generator.query.CustomPostgreSqlQuery;
import io.geekidea.boot.generator.query.CustomSqlServerQuery;
import lombok.Data;
import lombok.experimental.Accessors;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils;

import java.util.*;

/**
 * Spring Boot Plus 代码生成处理器
 *
 * @author geekidea
 * @date 2018-11-08
 */
@Slf4j
@Data
@Accessors(chain = true)
public class GenerateHandler {

    /**
     * 下划线专程驼峰命名
     * sys_user --> sysUser
     *
     * @param underline
     * @return
     */
    public static String underlineToCamel(String underline) {
        if (StringUtils.isNotBlank(underline)) {
            return NamingStrategy.underlineToCamel(underline);
        }
        return null;
    }

    /**
     * 下划线转换成帕斯卡命名
     * sys_user --> SysUser
     *
     * @param underline
     * @return
     */
    public static String underlineToPascal(String underline) {
        if (StringUtils.isNotBlank(underline)) {
            return NamingStrategy.capitalFirst(NamingStrategy.underlineToCamel(underline));
        }
        return null;
    }

    /**
     * 骆驼转换成帕斯卡命名
     * roleId --> RoleId
     *
     * @param camel
     * @return
     */
    public static String camelToPascal(String camel) {
        if (StringUtils.isNotBlank(camel)) {
            return NamingStrategy.capitalFirst(camel);
        }
        return null;
    }

    /**
     * 下划线转换成冒号连接命名
     * sys_user --> sys:user
     *
     * @param underline
     * @return
     */
    public static String underlineToColon(String underline) {
        if (StringUtils.isNotBlank(underline)) {
            String string = underline.toLowerCase();
            return string.replaceAll("_", ":");
        }
        return null;
    }

    /**
     * 帕斯卡转换成驼峰命名法
     * SysUser --> sysUser
     *
     * @param pascal
     * @return
     */
    public static String pascalToCamel(String pascal) {
        if (StringUtils.isNotBlank(pascal)) {
            return pascal.substring(0, 1).toLowerCase() + pascal.substring(1);
        }
        return pascal;
    }

    public static void exit() {
        System.exit(0);
    }

    /**
     * 获取括号中的内容
     *
     * @param string
     * @return
     */
    public static String getBracketsContent(String string) {
        if (StringUtils.isNotBlank(string)) {
            int start = string.indexOf("(");
            int end = string.lastIndexOf(")");
            if (start != -1) {
                String content = string.substring(start + 1, end);
                return content;
            }
        }
        return null;
    }

    /**
     * 判断括弧中的内容是否是小数
     *
     * @param string
     * @return
     */
    public static boolean isBracketsContentDecimal(String string) {
        String content = getBracketsContent(string);
        int index = content.indexOf(",");
        return index != -1;
    }

    /**
     * 生成代码
     */
    public void generator(GeneratorConfig config) {
        // 初始化
        config.init();
        // 循环生成
        Set<String> tableNames = config.getTableNames();
        for (String tableName : tableNames) {
            // 生成代码
            handle(config, tableName);
            log.info(tableName + " 代码生成成功");
        }
    }

    public void handle(GeneratorConfig config, String tableName) {
        // 代码生成器
        AutoGenerator mpg = new AutoGenerator();
        // 全局配置
        GlobalConfig gc = new GlobalConfig();
        String projectPath = System.getProperty("user.dir");
        gc.setOutputDir(projectPath + "/src/main/java");
        gc.setAuthor(config.getAuthor());
        gc.setOpen(false);
        gc.setSwagger2(true);
        gc.setIdType(config.getIdType());
        gc.setServiceName("%sService");
        gc.setFileOverride(config.isFileOverride());
        gc.setDateType(DateType.ONLY_DATE);
        gc.setBaseResultMap(config.isBaseResultMap());

        mpg.setGlobalConfig(gc);

        // 数据源配置
        DataSourceConfig dsc = new DataSourceConfig();
        DatabaseType databaseType = config.getDatabaseType();
        if (databaseType == null) {
            throw new BusinessException("数据库类型不能为空");
        }
        if (databaseType == DatabaseType.MYSQL) {
            dsc.setDbType(DbType.MYSQL);
        } else if (databaseType == DatabaseType.MARIADB) {
            dsc.setDbType(DbType.MARIADB);
        } else if (databaseType == DatabaseType.ORACLE) {
            dsc.setDbType(DbType.ORACLE);
        } else if (databaseType == DatabaseType.ORACLE_12C) {
            dsc.setDbType(DbType.ORACLE);
        } else if (databaseType == DatabaseType.POSTGRE_SQL) {
            dsc.setDbType(DbType.POSTGRE_SQL);
        } else if (databaseType == DatabaseType.SQL_SERVER) {
            dsc.setDbType(DbType.SQL_SERVER);
        } else if (databaseType == DatabaseType.SQL_SERVER2005) {
            dsc.setDbType(DbType.SQL_SERVER2005);
        }
        dsc.setUrl(config.getUrl());
        dsc.setDriverName(config.getDriverName());
        dsc.setUsername(config.getUsername());
        dsc.setPassword(config.getPassword());
        dsc.setSchemaName(config.getSchema());
        // 设置自定义查询
        if (databaseType == DatabaseType.MYSQL || databaseType == DatabaseType.MARIADB) {
            // Mysql
            dsc.setDbQuery(new CustomMySqlQuery());
        } else if (databaseType == DatabaseType.ORACLE || databaseType == DatabaseType.ORACLE_12C) {
            // Oracle
            dsc.setDbQuery(new CustomOracleQuery());
        } else if (databaseType == DatabaseType.SQL_SERVER || databaseType == DatabaseType.SQL_SERVER2005) {
            // SQL Server
            dsc.setDbQuery(new CustomSqlServerQuery());
        } else if (databaseType == DatabaseType.POSTGRE_SQL) {
            // Postgresql
            dsc.setDbQuery(new CustomPostgreSqlQuery());
        }
        mpg.setDataSource(dsc);

        // 包配置
        PackageConfig pc = new PackageConfig();
        pc.setModuleName(config.getModuleName());
        pc.setParent(config.getParentPackage());
        pc.setController(config.getPackageController());

        mpg.setPackageInfo(pc);

        // 默认的分页排序类型
        DefaultOrderType defaultOrderType = config.getDefaultOrderType();
        // 创建时间列名称
        final String createTimeColumn = config.getCreateTimeColumn();
        // 修改时间列名称
        final String updateTimeColumn = config.getUpdateTimeColumn();
        // 是否生成权限注解
        final boolean permission = config.isPermission();
        // 是否生成简单模式
        final boolean simple = config.isSimple();
        // 自定义配置
        InjectionConfig cfg = new InjectionConfig() {

            @Override
            public void initTableMap(TableInfo tableInfo) {
                try {
                    super.initTableMap(tableInfo);
                    if (!tableInfo.isHavePrimaryKey()) {
                        throw new BusinessException(tableName + " 表主键不能为空");
                    }
                    String tableName = tableInfo.getName();
                    String colonTableName = underlineToColon(tableName);
                    Map<String, Object> map = new HashMap<>();
                    String entityName = tableInfo.getEntityName();
                    String entityObjectName = pascalToCamel(entityName);
                    map.put("entityObjectName", entityObjectName);

                    String serviceName = tableInfo.getServiceName();
                    String mapperName = tableInfo.getMapperName();
                    String serviceObjectName = pascalToCamel(serviceName);
                    String mapperObjectName = pascalToCamel(mapperName);
                    map.put("serviceObjectName", serviceObjectName);
                    map.put("mapperObjectName", mapperObjectName);

                    List<TableField> tableFields = tableInfo.getFields();
                    if (CollectionUtils.isEmpty(tableFields)) {
                        throw new BusinessException(tableName + " 表列不能为空");
                    }
                    String pkIdColumnName = null;
                    boolean existsCreateTimeColumn = false;
                    boolean existsUpdateTimeColumn = false;
                    String updateTimeColumnName = null;
                    boolean existsDateType = false;
                    boolean existsBigDecimalType = false;
                    for (TableField tableField : tableFields) {
                        String columnName = tableField.getColumnName();
                        String propertyName = tableField.getPropertyName();
                        boolean isKeyFlag = tableField.isKeyFlag();
                        if (isKeyFlag) {
                            pkIdColumnName = columnName;
                            map.put("pkIdColumnName", pkIdColumnName);
                            String pkIdName = propertyName;
                            if (StringUtils.isNotBlank(pkIdName)) {
                                String pascalPkIdName = camelToPascal(pkIdName);
                                map.put("pascalPkIdName", pascalPkIdName);
                                map.put("pkIdName", pkIdName);
                            }
                        } else {
                            if (StringUtils.isNotBlank(createTimeColumn) && createTimeColumn.equalsIgnoreCase(columnName)) {
                                existsCreateTimeColumn = true;
                            } else if (StringUtils.isNotBlank(updateTimeColumn) && updateTimeColumn.equalsIgnoreCase(columnName)) {
                                existsUpdateTimeColumn = true;
                                updateTimeColumnName = columnName;
                            }
                        }
                        String tableFieldType = tableField.getType();
                        IColumnType columnType = tableField.getColumnType();
                        String propertyType = tableField.getPropertyType();
                        if (propertyType.equals("Date")) {
                            existsDateType = true;
                        } else if (propertyType.equals("BigDecimal")) {
                            existsBigDecimalType = true;
                        }
                        // 处理自定义属性值
                        Map<String, Object> customMap = tableField.getCustomMap();
                        if (MapUtils.isEmpty(customMap)) {
                            customMap = new HashMap<>();
                        }
                        if (databaseType == DatabaseType.MYSQL || databaseType == DatabaseType.MARIADB) {
                            // MySQL处理长度
                            String type = tableField.getType();
                            String length = getBracketsContent(type);
                            customMap.put("length", length);
                            // MySQL处理空值和默认值
                            String nullValue = MapUtils.getString(customMap, "null");
                            String defaultValue = MapUtils.getString(customMap, "default");
                            if ("YES".equals(nullValue)) {
                                customMap.put("nullable", true);
                            } else {
                                customMap.put("nullable", false);
                            }
                            customMap.put("defaultValue", defaultValue);
                        } else if (databaseType == DatabaseType.ORACLE || databaseType == DatabaseType.ORACLE_12C) {
                            // Oracle处理长度
                            Integer dataLength = MapUtils.getInteger(customMap, "DATA_LENGTH");
                            customMap.put("length", dataLength);
                            // Oracle处理空值和默认值
                            String nullable = MapUtils.getString(customMap, "NULLABLE");
                            String dataDefault = MapUtils.getString(customMap, "DATA_DEFAULT");
                            if ("Y".equals(nullable)) {
                                customMap.put("nullable", true);
                            } else {
                                customMap.put("nullable", false);
                            }
                            customMap.put("defaultValue", dataDefault);
                        } else if (databaseType == DatabaseType.POSTGRE_SQL) {
                            // postgresql处理长度
                            Integer length = MapUtils.getInteger(customMap, "length");
                            customMap.put("length", length);
                            // postgresql处理空值和默认值
                            String nullable = MapUtils.getString(customMap, "attnotnull");
                            String dataDefault = MapUtils.getString(customMap, "default_value");
                            if (nullable.contains("true")) {
                                customMap.put("nullable", false);
                            } else {
                                customMap.put("nullable", true);
                            }
                            customMap.put("defaultValue", dataDefault);
                        } else if (databaseType == DatabaseType.SQL_SERVER || databaseType == DatabaseType.SQL_SERVER2005) {
                            // sqlserver处理长度
                            Integer length = MapUtils.getInteger(customMap, "max_length");
                            customMap.put("length", length);
                            // sqlserver处理空值和默认值
                            String nullable = MapUtils.getString(customMap, "is_nullable");
                            String dataDefault = MapUtils.getString(customMap, "default_value");
                            if (nullable.contains("false")) {
                                customMap.put("nullable", false);
                            } else {
                                customMap.put("nullable", true);
                            }
                            customMap.put("defaultValue", dataDefault);
                        }
                    }
                    String defaultOrderColumnName = null;
                    if (DefaultOrderType.PK_ID == defaultOrderType) {
                        defaultOrderColumnName = pkIdColumnName;
                    } else if (DefaultOrderType.CREATE_TIME == defaultOrderType) {
                        if (existsCreateTimeColumn) {
                            defaultOrderColumnName = createTimeColumn;
                        }
                    }
                    map.put("defaultOrderType", defaultOrderType);
                    map.put("defaultOrderColumnName", defaultOrderColumnName);

                    // 修改列名称
                    if (existsUpdateTimeColumn) {
                        String pascalUpdateTimeColumnName = underlineToPascal(updateTimeColumnName);
                        map.put("pascalUpdateTimeColumnName", pascalUpdateTimeColumnName);
                    }

                    // 是否存在日期类型
                    map.put("existsDateType", existsDateType);
                    // 是否存在BigDecimal
                    map.put("existsBigDecimalType", existsBigDecimalType);

                    // 包路径
                    // dto
                    String dtoPackage = config.getDtoPackage();
                    map.put("dtoPackage", dtoPackage);
                    map.put("addDtoPath", dtoPackage + "." + entityName + "AddDto");
                    map.put("updateDtoPath", dtoPackage + "." + entityName + "UpdateDto");
                    // vo
                    String voPackage = config.getVoPackage();
                    map.put("voPackage", voPackage);
                    map.put("voPath", voPackage + "." + entityName + "Vo");
                    map.put("infoVoPath", voPackage + "." + entityName + "InfoVo");
                    // query
                    String queryPackage = config.getQueryPackage();
                    map.put("queryPackage", queryPackage);
                    map.put("queryPath", queryPackage + "." + entityName + "Query");
                    // 导入排序查询参数类
                    map.put("superQueryPath", config.getSuperQuery());
                    // service对象名称
                    map.put("serviceObjectName", entityObjectName + "Service");
                    // mapper对象名称
                    map.put("mapperObjectName", entityObjectName + "Mapper");
                    map.put("commonFields", config.getCommonFields());
                    map.put("voExcludeFields", config.getVoExcludeFields());
                    // 冒号连接的表名称
                    map.put("colonTableName", colonTableName);
                    // 是否生成permission注解
                    map.put("permission", permission);
                    map.put("simple", simple);

                    // 导入分页类
                    map.put("paging", config.getCommonPaging());
                    // 导入排序枚举
                    map.put("orderEnum", config.getCommonOrderEnum());
                    // ApiResult
                    map.put("apiResult", config.getCommonApiResult());
                    // 分页列表查询是否排序
                    map.put("pageListOrder", config.isPageListOrder());
                    map.put("businessException", config.getCommonBusinessException());
                    map.put("orderByItem", config.getCommonOrderByItem());

                    // 代码生成策略
                    //  map.put("generatorStrategy", generatorStrategy);
                    // 代码Validation校验
                    map.put("paramValidation", config.isParamValidation());

                    this.setMap(map);
                } catch (
                        Exception e) {
                    e.printStackTrace();
                    exit();
                }
            }

            @Override
            public void initMap() {

            }
        };
        List<FileOutConfig> focList = new ArrayList<>();

        // 生成mapper xml
        if (config.isGeneratorMapperXml()) {
            focList.add(new FileOutConfig("/templates/mapper.xml.vm") {
                @Override
                public String outputFile(TableInfo tableInfo) {
                    // 自定义输入文件名称
                    return projectPath + "/src/main/resources/mapper/" + pc.getModuleName()
                            + "/" + tableInfo.getEntityName() + "Mapper" + StringPool.DOT_XML;
                }
            });
        }

        String projectPackagePath = config.getProjectPackagePath();
        // 自定义addDto
        if (config.isGeneratorAddDto()) {
            focList.add(new FileOutConfig("/templates/addDto.java.vm") {
                @Override
                public String outputFile(TableInfo tableInfo) {
                    return projectPath + "/src/main/java/" + projectPackagePath + "/" + pc.getModuleName() + "/dto/" + tableInfo.getEntityName() + "AddDto" + StringPool.DOT_JAVA;
                }
            });
        }
        // 自定义updateDto
        if (config.isGeneratorUpdateDto()) {
            focList.add(new FileOutConfig("/templates/updateDto.java.vm") {
                @Override
                public String outputFile(TableInfo tableInfo) {
                    return projectPath + "/src/main/java/" + projectPackagePath + "/" + pc.getModuleName() + "/dto/" + tableInfo.getEntityName() + "UpdateDto" + StringPool.DOT_JAVA;
                }
            });
        }
        // 自定义query
        if (config.isGeneratorQuery()) {
            focList.add(new FileOutConfig("/templates/query.java.vm") {
                @Override
                public String outputFile(TableInfo tableInfo) {
                    return projectPath + "/src/main/java/" + projectPackagePath + "/" + pc.getModuleName() + "/query/" + tableInfo.getEntityName() + "Query" + StringPool.DOT_JAVA;
                }
            });
        }
        // 自定义vo
        if (config.isGeneratorVo()) {
            focList.add(new FileOutConfig("/templates/vo.java.vm") {
                @Override
                public String outputFile(TableInfo tableInfo) {
                    return projectPath + "/src/main/java/" + projectPackagePath + "/" + pc.getModuleName() + "/vo/" + tableInfo.getEntityName() + "Vo" + StringPool.DOT_JAVA;
                }
            });
        }
        // 自定义infoVo
        if (config.isGeneratorInfoVo()) {
            focList.add(new FileOutConfig("/templates/infoVo.java.vm") {
                @Override
                public String outputFile(TableInfo tableInfo) {
                    return projectPath + "/src/main/java/" + projectPackagePath + "/" + pc.getModuleName() + "/vo/" + tableInfo.getEntityName() + "InfoVo" + StringPool.DOT_JAVA;
                }
            });
        }

        cfg.setFileOutConfigList(focList);
        mpg.setCfg(cfg);

        // 模版生成配置，设置为空，表示不生成
        TemplateConfig templateConfig = new TemplateConfig();
        // xml使用自定义输出
        templateConfig.setXml(null);
        // 是否生成entity
        if (!config.isGeneratorEntity()) {
            templateConfig.setEntity(null);
        }
        // 是否生成controller
        if (!config.isGeneratorController()) {
            templateConfig.setController(null);
        }
        // 是否生成service
        if (!config.isGeneratorService()) {
            templateConfig.setService(null);
        }
        // 是否生成serviceImpl
        if (!config.isGeneratorServiceImpl()) {
            templateConfig.setServiceImpl(null);
        }
        // 是否生成mapper
        if (!config.isGeneratorMapper()) {
            templateConfig.setMapper(null);
        }
        mpg.setTemplate(templateConfig);

        // 策略配置
        StrategyConfig strategy = new StrategyConfig();
        strategy.setNaming(NamingStrategy.underline_to_camel);
        strategy.setColumnNaming(NamingStrategy.underline_to_camel);
        strategy.setEntityLombokModel(true);
        strategy.setRestControllerStyle(true);
        strategy.setSuperServiceClass(config.getSuperService());
        strategy.setSuperServiceImplClass(config.getSuperServiceImpl());
        strategy.setInclude(tableName);
        strategy.setSuperEntityColumns(config.getSuperEntityCommonColumns());
        strategy.setControllerMappingHyphenStyle(true);
        strategy.setTablePrefix(config.getTablePrefix());
        mpg.setStrategy(strategy);
        mpg.execute();
    }


}
